﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Data;
using System.Windows.Documents;
using System.Windows.Input;
using System.Windows.Media;
using System.Windows.Media.Imaging;
using System.Windows.Navigation;
using System.Windows.Shapes;
using SlimDX.Direct3D9;
using System.Runtime.InteropServices;
using SlimDX;
using RaygunDiplomacy.Graphics;

namespace RaygunDiplomacy
{
   /// <summary>
   /// Interaction logic for MainWindow.xaml
   /// </summary>
   public partial class MainWindow : Window
   {
      #region Fields

      DirectxDisplay display;

      DelegateRenderer3D testRenderer;

      TimeSpan lastRender;

      public bool SpinClockwise { get; set; }

      #endregion

      public MainWindow()
      {
         InitializeComponent();
      }

      void CompositionTarget_Rendering(object sender, EventArgs e)
      {
         RenderingEventArgs args = (RenderingEventArgs)e;

         // It's possible for Rendering to call back twice in the same frame 
         // so only render when we haven't already rendered in this frame.
         if (d3dImage.IsFrontBufferAvailable && lastRender != args.RenderingTime)
         {
            display.Render();
            lastRender = args.RenderingTime;
         }
      }
      
      private void MainWindow_Loaded(object sender, RoutedEventArgs args)
      {
         GraphicsProvider instance = GraphicsProvider.Instance;

         if (instance.Device == null)
         {
            try
            {
               instance.Initialize();
            }
            catch (Exception e)
            {
               this.testBox.Text = e.ToString();
               return;
            }

            if (instance.IsVistaOr7)
            {
               if (instance.DeviceEx != null)
               {
                  testBox.Text = "Succcessfully created DeviceEx";
               }
               else
               {
                  testBox.Text = "Failed to create DeviceEx";
               }
            }
            else
            {
               if (instance.Device != null)
               {
                  testBox.Text = "Succcessfully created Device";
               }
               else
               {
                  testBox.Text = "Failed to create Device";
               }
            }
         }

         try
         {
            display = new DirectxDisplay(this.d3dImage, this.imageHost);
            
            display.Initialize();
            SetupTestRenderer();

            CompositionTarget.Rendering += new EventHandler(CompositionTarget_Rendering);
         }
         catch (Exception e)
         {
            testBox.Text += "\n" + e.ToString();
         }
      }

      private void reverseButton_Click(object sender, RoutedEventArgs e)
      {
         this.SpinClockwise = !this.SpinClockwise;
      }

      #region Set up Test Renderer

      [StructLayout(LayoutKind.Sequential)]
      struct CustomVertex
      {
         public Vector3 position;
         public int color;

         public static int Size
         {
            get
            {
               return Vector3.SizeInBytes + sizeof(int);
            }
         }

         public static VertexFormat VertexFormat
         {
            get { return SlimDX.Direct3D9.VertexFormat.Position | SlimDX.Direct3D9.VertexFormat.Diffuse; }
         }
      }

      class TestProperties
      {
         public SlimDX.Matrix world = SlimDX.Matrix.Identity;
         public float angle = 0;
         public int lastMillSec = DateTime.Now.Millisecond;
      }

      private void SetupTestRenderer()
      {
         #region Render Data

         CustomVertex[] triangle = new CustomVertex[] { 
            new CustomVertex() { position = new Vector3(1, 0, 0), color = System.Drawing.Color.Red.ToArgb() }, 
            new CustomVertex() { position = new Vector3(0, 1, 0), color = System.Drawing.Color.Green.ToArgb() }, 
            new CustomVertex() { position = new Vector3(0, 0, -1), color = System.Drawing.Color.Yellow.ToArgb() }, 
         };

         #region Reset

         ResourceHandler<VertexBuffer> dataReset = delegate(ref VertexBuffer triangleBuffer)
               {
                  GraphicsProvider provider = GraphicsProvider.Instance;

                  if (triangleBuffer != null && !triangleBuffer.Disposed)
                  {
                     triangleBuffer.Dispose();
                     triangleBuffer = null;
                  }

                  int size = CustomVertex.Size * 3;

                  triangleBuffer = new VertexBuffer(provider.Device, size, Usage.WriteOnly, CustomVertex.VertexFormat, Pool.Default);

                  DataStream data = triangleBuffer.Lock(0, size, LockFlags.None);

                  data.WriteRange<CustomVertex>(triangle);

                  triangleBuffer.Unlock();
               };

         #endregion

         #region Release and Dispose
         ResourceHandler<VertexBuffer> dataReleaseAndDispose = delegate(ref VertexBuffer triangleBuffer)
         {
            if (triangleBuffer != null)
            {
               if (!triangleBuffer.Disposed)
               {
                  triangleBuffer.Dispose();
               }
               triangleBuffer = null;
            }
         };
#endregion

         DelegateGraphicsResource<VertexBuffer> testData =
            new DelegateGraphicsResource<VertexBuffer>(dataReset, dataReleaseAndDispose, dataReleaseAndDispose);

         testData.Reset();

         #endregion

         #region Renderer

         //#region Update

         //Action<TimeSpan, DateTime> update = (elapsed, currentTime) =>
         //   {
         //   };

         //#endregion

         #region Render

         TestProperties testProperties = new TestProperties();

         Action<Camera> render = camera =>
            {
               Result result;
               GraphicsProvider provider = GraphicsProvider.Instance;

               #region World
               int millSec = DateTime.Now.Millisecond;

               testProperties.angle += (millSec - testProperties.lastMillSec) * (2.0f * (float)Math.PI) / 1000.0f * (SpinClockwise ? -1 : 1);

               testProperties.lastMillSec = millSec;

               var world = SlimDX.Matrix.RotationY(testProperties.angle);

               result = provider.Device.SetTransform(TransformState.World, world);
               if (result.IsFailure)
               {
                  Console.WriteLine("SetTransform(World) failed");
               }
               #endregion

               result = provider.Device.SetStreamSource(0, testData.Resource, 0, CustomVertex.Size);
               if (result.IsFailure)
               {
                  Console.WriteLine("SetStreamSource failed");
               }

               provider.Device.VertexFormat = CustomVertex.VertexFormat;

               result = provider.Device.DrawPrimitives(PrimitiveType.TriangleList, 0, 1);
               if (result.IsFailure)
               {
                  Console.WriteLine("DrawPrimitives failed");
               }

               CustomVertex[] temp = new CustomVertex[2];
               temp[0].color = System.Drawing.Color.White.ToArgb();
               temp[0].position = new Vector3(0, 0, 0);
               temp[1].color = System.Drawing.Color.Yellow.ToArgb();
               temp[1].position = new Vector3(1, 0, 0);

               provider.Device.DrawUserPrimitives<CustomVertex>(PrimitiveType.LineList, 1, temp);
            };

         #endregion

         testRenderer = new DelegateRenderer3D(render, null, null, () => testData.Dispose());

         #endregion

         display.AddRenderer(testRenderer);
      }

      #endregion
   }
}
